using Godot;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;

namespace DragDemo.DragablePanels
{
	///<summary>拖拽竖向的StackPanel</summary>
	public partial class DragableVerticalStackPanel : DragablePanelBase
	{
		#region props

		private List<Control> _children = new List<Control>();
		private List<Control> _childrenIgnoreDragObject = new List<Control>();
		private Control _parent;

		private Vector2 _offset;
		private Control _currentDragControl;
		private int _lastInsertIndex = -1;


		private double IntervalSpace = 10;


		#endregion

		#region ctors

		public DragableVerticalStackPanel()
		{
			TreeEntered += DragableVerticalStackPanel_TreeEntered;
		}

		#endregion

		#region event impls

		private void DragableVerticalStackPanel_TreeEntered()
		{
			var tree = GetParent<Control>();
			tree.ChildEnteredTree += Tree_ChildEnteredTree;
			tree.ChildExitingTree += Tree_ChildExitingTree;
			tree.ChildOrderChanged += Tree_ChildOrderChanged;

			_parent = tree;
		}

		#endregion


		#region overrides

		public override void _Ready()
		{
			base._Ready();
			Arrange();
		}

		public override void Arrange()
		{
			var sum = -IntervalSpace;
			foreach (var child in _children)
			{
				var height = child.Size.Y;
				var y = IntervalSpace + sum;
				child.Position = new Vector2(_parent.Position.X, (float)y);
				sum = y + height;
			}
		}

		public void ArrangeInDrag()
		{
			var sum = -IntervalSpace;
			foreach (var child in _children)
			{
				var height = child.Size.Y;
				var y = IntervalSpace + sum;

				if (child != _currentDragControl)
					child.Position = new Vector2(_parent.Position.X, (float)y);
				sum = y + height;
			}
		}

		public void ArrangeInDragTween()
		{
			//if (_tween == null) return;
			var sum = -IntervalSpace;
			foreach (var child in _children)
			{
				var height = child.Size.Y;
				var y = IntervalSpace + sum;

				if (child != _currentDragControl)
				{
					var tween = child.CreateTween();
					tween.SetTrans(Tween.TransitionType.Quart);
					tween.SetEase(Tween.EaseType.Out);
					var targetPos = new Vector2(_parent.Position.X, (float)y);
					tween.TweenProperty(child,"position", targetPos, 0.2d);
				}

				sum = y + height;
			}
		}

		public override void _Process(double delta)
		{
			base._Process(delta);

			//GD.Print(Input.IsActionJustPressed(_actionName));

			// 刚按下时。
			if (Input.IsActionJustPressed(_actionName))
			{
				DragStart();
			}
			// 拖动时。
			else if (Input.IsActionPressed(_actionName))
			{
				DragMove();
			}
			else if (Input.IsActionJustReleased(_actionName))
			{
				Arrange();
			}

		}

		#endregion

		#region drag start

		private void DragStart()
		{
			var mousePos = GetViewport().GetViewport().GetMousePosition();
			var hitResult = HitTest(mousePos);
			if (hitResult == null) return;

			// 设置当前拖拽的内容。
			_currentDragControl = hitResult;

			_offset = _currentDragControl.Position - mousePos;
			_currentDragControl.Position = mousePos + _offset;

			_childrenIgnoreDragObject = _children.Where(c => c != hitResult).ToList();

			_lastInsertIndex = GetInsertIndex(_parent, _childrenIgnoreDragObject, hitResult, mousePos.Y, IntervalSpace);
		}

		private void DragMove()
		{
			if (_currentDragControl == null) return;
			var mousePos = GetViewport().GetViewport().GetMousePosition();
			_currentDragControl.Position = mousePos + _offset;

			var currentInsertIndex = GetInsertIndex(_parent, _childrenIgnoreDragObject, _currentDragControl, mousePos.Y, IntervalSpace);

			if (currentInsertIndex != _lastInsertIndex)
			{
				_lastInsertIndex = currentInsertIndex;

				OnInsertIndexChanged(currentInsertIndex, _currentDragControl);
			}

		}

		///<summary>当下标发生更改</summary>
		private void OnInsertIndexChanged(int newIndex, Control newControl)
		{
			var list = new List<Control>(_childrenIgnoreDragObject);
			newIndex = Math.Clamp(newIndex, 0, list.Count);
			list.Insert(newIndex, newControl);
			_children = list;
			
			ArrangeInDragTween();
		}

		private int GetInsertIndex(Control parent, IList<Control> controls, Control currentDragControl, double posY, double space)
		{
			var collectionY = parent.Position.Y;

			if (space < collectionY) return 0;

			var sum = collectionY;
			var flag = 0;
			foreach (var control in controls)
			{
				if (control == currentDragControl) continue;

				var height = control.Size.Y;
				var controlStartY = sum;
				var controlEndY = controlStartY + height;

				var controlSpacePartEndY = controlEndY + space / 2;
				var controlSpacePartStartY = controlStartY - space / 2;

				if (posY >= controlSpacePartStartY && posY < controlSpacePartEndY)
				{
					var controlCenterX = controlStartY + height / 2;

					if (posY < controlCenterX) return controls.IndexOf(control) + flag;
					return controls.IndexOf(control) + 1 + flag;
				}

				sum = controlEndY + (float)space;
			}

			return controls.Count() + 1;
		}

		#endregion

		#region drag input

		private Control HitTest(Vector2 point)
		{
			//var isInRect = IsInRect(_parent, point);
			//if (isInRect == false) return null; // 在整个控件外时。

			foreach (var child in _children)
			{
				var isInChildRect = IsInRect(child, point);
				if (isInChildRect == true) return child;
			}

			return null;
		}

		private bool IsInRect(Control control, Vector2 point)
		{
			var size = control.Size;
			var pos = control.Position;
			var rect = new Rect2(pos, size);
			return rect.HasPoint(point);
		}


		#endregion



		#region methods

		///<summary>当前容器内容发生更改时。</summary>
		private void ReloadChildren()
		{
			if (_parent == null) return;

			var childCount = _parent.GetChildCount(false);

			var result = new List<Control>();
			for (int i = 0; i < childCount; i++)
			{
				var child = _parent.GetChildOrNull<Control>(i);
				if (child == null) continue;
				result.Add(child);
			}

			_children = result;

		}


		#endregion


		#region child changed

		private void Tree_ChildOrderChanged()
		{
			ReloadChildren();
		}

		private void Tree_ChildExitingTree(Node node)
		{
			ReloadChildren();
		}

		private void Tree_ChildEnteredTree(Node node)
		{
			ReloadChildren();
		}


		#endregion
	}
}
